
package main;

import iterators.FixpointIterator;
import iterators.IterationPhase;
import iterators.RecursiveIterator;
import iterators.RoundRobin;
import iterators.Worklist;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

import javax.xml.soap.Node;

import config.AnalysisTypes;
import config.GlobalConfig;
import config.OutputFormat;
import controlFlowGraph.CFG;
import controlFlowGraph.CFGNode;
import domains.AvailableExpressionsAnalysis;
import domains.IntervalAnalysis;
import domains.TrulyLiveVariablesAnalysis;

import parser.Lexer;
import parser.Parser;

public class ProgramAnalyzer {

	public static void main(String[] args) throws Exception {
		boolean debug = true;
		String file = "";
		String programCode = "";
		Lexer lex;
		Parser parser;
		if (args.length == 0) {
			throw new Exception("No input files");
		}
		try {
			file = args[0];
			programCode = readFile(file, "Quell-Datei");
			lex = new Lexer(programCode);
			if (GlobalConfig.debug) {
				while (lex.hasNext()) {
					System.out.println(lex.next());
				}
				lex = new Lexer(programCode);
			}
			parser = new Parser(lex);
			CFG cfg = parser.parse();
			if (GlobalConfig.debug) {
				System.out.println(GlobalConfig.variableSet);
				System.out.println(GlobalConfig.expressionSet);
				System.out.println(cfg.toGraphVisualization());
			}

			switch (GlobalConfig.analysisType) {
			case AvailableExpressions:
				GlobalConfig.analysis = new AvailableExpressionsAnalysis();
				break;
			case TrulyLiveVariables:
				GlobalConfig.analysis = new TrulyLiveVariablesAnalysis();
				break;
			case IntervalAnalysis:
				GlobalConfig.analysis = new IntervalAnalysis();
				GlobalConfig.iterPhase = IterationPhase.WIDENING;
				break;
			default:
				System.out.println("Analysis Type not supported!");
			}

			if (GlobalConfig.outputFormat == OutputFormat.TRANSFORMATION) {
				GlobalConfig.analysis.preTransformCFG(cfg);
				if (GlobalConfig.debug) {
					System.out.println("After transformation: ");
					System.out.println(GlobalConfig.variableSet);
					System.out.println(GlobalConfig.expressionSet);
					System.out.println(cfg.getAllNodes());
					System.out.println(cfg.toGraphVisualization());
				}
				
			}
			
			GlobalConfig.analysis.initialize(cfg);

			// doing iterations
			FixpointIterator iterator;
			
			switch (GlobalConfig.algorithm) {
			case ROUNDROBIN:
				iterator = new RoundRobin();
				break;
			case WORKLIST:
				iterator = new Worklist();
				break;
			case RECURSIVE:
				iterator = new RecursiveIterator();
				break;
			default:
				iterator = new RoundRobin();
			}
			
			iterator.doIteration(cfg);

			if (GlobalConfig.analysisType == AnalysisTypes.AvailableExpressions) {
				((AvailableExpressionsAnalysis) GlobalConfig.analysis)
						.removeTrivialExpressions(cfg);
			}

			// If widening, then narrowing ;-)
			if (GlobalConfig.iterPhase == IterationPhase.WIDENING) {
				GlobalConfig.iterPhase = IterationPhase.NARROWING;
				iterator.doIteration(cfg);
			}
			
			/** Ausgabe **/
			if (GlobalConfig.outputFormat == OutputFormat.ANALYSIS) {
				System.out.println("ANALYSIS_RESULTS");
				for (CFGNode node : cfg.getAllNodes()) {
					System.out.println("   "
							+ GlobalConfig.analysis.printNode(node));
				}
			} else {
				/** Transformation **/
				if (GlobalConfig.debug) {
					System.out.println("ANALYSIS_RESULTS");
					for (CFGNode node : cfg.getAllNodes()) {
						System.out.println("   "
								+ GlobalConfig.analysis.printNode(node));
					}
				}
				GlobalConfig.analysis.postTransformCFG(cfg);
				if (GlobalConfig.debug) {
					System.out.println(cfg.toGraphVisualization());
				}
				System.out.println(cfg.asOutput());
			}
		} catch (Exception e) {
			System.out.println(e.getMessage());
			if (debug) {
				e.printStackTrace();
				System.out.println();
			}
		}
	}

	/**
	 * Reads the source-code File to a String
	 * 
	 * @param codeFile
	 *            , where the sourceCodeFile is located
	 * @return the string representation of the source-code
	 */
	public static String readFile(String codeFile, String dat) {
		String ruckgabe = "";
		File filename = new File(codeFile);
		boolean doesexist = filename.exists();
		if (doesexist) {
			boolean readable = filename.canRead();
			if (readable) {
				try {
					FileReader reader = new FileReader(filename);
					BufferedReader input = new BufferedReader(reader);
					while (input.ready()) {
						ruckgabe = ruckgabe + input.readLine() + "\n";
					}
					input.close();
					reader.close();
				} catch (IOException e) {
					System.err.println("Internal Error while reading file");
					e.printStackTrace();
					System.exit(-1);
				}
			} else {
				System.err.println(dat + " kann nicht gelesen werden");
				System.exit(-1);
			}
		} else {
			System.err.println(dat + " existiert nicht: " + codeFile + "!");
			System.exit(-1);
		}
		return ruckgabe;
	}

	private static void writeOutput(String targetCodeFile, String targetCode)
			throws Exception {
		File filename = new File(targetCodeFile);
		if (filename.exists()) {
			throw new Exception("Output File exists already");
		}
		try {
			FileWriter writer = new FileWriter(filename, true);
			BufferedWriter output = new BufferedWriter(writer);
			output.write(targetCode);
			output.close();
			writer.close();
		} catch (IOException e) {
			System.err
					.println("Internal Error, please report saarinformatix [at]"
							+ "googlemail [dot] com");
			e.printStackTrace();
			System.exit(-1);
		}
	}

}
